package com.kuang.auth;

import com.kuang.common.err.ValidationException;
import com.kuang.entity.User;
import com.kuang.handler.token.AuthorizationInterceptor;
import com.kuang.local.UserThreadLocal;
import com.kuang.service.JwtService;
import com.kuang.service.UserService;
import com.kuang.vo.AuthResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * @author 飞哥yykk
 * 更多学习关注飞哥B站
 * 地址是：https://space.bilibili.com/490711252
 * @title: CheckLoginAspect
 * @projectName tinyid
 * @description: TODO
 * @date 2021/9/621:43
 */
@Component
@Aspect // 代表当前是一个AOP切面类
//@Order(999999)
public class CheckLoginAspect implements Ordered {

    private static final Logger log = LoggerFactory.getLogger(CheckLoginAspect.class);

    private User user = new User();

    @Autowired
    private JwtService jwtService;
    @Autowired
    private UserService userService;

    private static final String AUTH = "Authorization";
    private static final String AUTH_USERNAME = "ksd-user-name";

    // 1: 定义CheckLogin切点
    @Pointcut("@annotation(com.kuang.auth.CheckLogin)")
    public void checkLoginPoint() {
    }

    // 2: 定义CheckAdminLogin切点
    @Pointcut("@annotation(com.kuang.auth.CheckAdminLogin)")
    public void checkLoginAdminPoint() {
    }

    /******************************** checkLogin *************************/
    // 3 : 定义对应执行通知 @Before 、@After、@Aroud、@Throwing
    @Before("checkLoginPoint()")
    public void beforeAdvice(JoinPoint joinPoint) {
        log.info("1-----------beforeAdvice --- 进来了....");
        this.checkLogin();
    }

    // 4 : 定义对应执行通知 @Before 、@After、@Aroud、@Throwing
    @After("checkLoginPoint()")
    public void afterAdvice(JoinPoint joinPoint) {
        UserThreadLocal.remove();
    }
    /******************************** checkLogin *************************/


    /******************************** checkLoginAdmin *************************/
    // 3 : 定义对应执行通知 @Before 、@After、@Aroud、@Throwing
    @Before("checkLoginAdminPoint()")
    public void beforeAdminAdvice(JoinPoint joinPoint) {
        log.info("1-----------beforeAdminAdvice --- 进来了....");
        this.checkLogin();
        // 1: 获取用户角色
        User user = UserThreadLocal.get();
        String role = user.getRole();
        if (StringUtils.isEmpty(role)) {
            throw new ValidationException(302, "角色不能为空");
        }
        // 2 : 如何拿到方法上的注解呢？
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        // 拿到执行方法 ---- 通过反射拿到注解---拿到注解就可以拿到参数
        CheckAdminLogin checkAdminLogin = method.getDeclaredAnnotation(CheckAdminLogin.class);
        if (checkAdminLogin != null) {
            // 拿到注解参数
            String anovalue = checkAdminLogin.value();
            if (!Objects.equals(role,anovalue )) {
                throw new ValidationException(302, "权限不足!!!");
            }
        }
    }

    // 4 : 定义对应执行通知 @Before 、@After、@Aroud、@Throwing
    @After("checkLoginAdminPoint()")
    public void afterAdminAdvice(JoinPoint joinPoint) {
        UserThreadLocal.remove();
    }

    /******************************** checkLoginAdmin *************************/


    public static String getParam(HttpServletRequest request, String filedName) {
        //1 :在参数里面区获取对应filedName的值
        String param = request.getParameter(filedName);
        // 2:如果不存在
        if (StringUtils.isEmpty(param)) {
            // 3: 就请求头区获取对应filedName的值
            param = request.getHeader(filedName);
        }
        return param;
    }

    private void checkLogin() {
        log.info("AOP拦截进来了....................");
        // 1：获取服务请求的对象
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        HttpServletResponse response = requestAttributes.getResponse();
        // 2: 获取请求头中的token信息
        String openid = getParam(request, AUTH_USERNAME);
        String token = getParam(request, AUTH);

        if (StringUtils.isEmpty(token)) {
            throw new ValidationException(300, "Authorization不允许为空，请重新登录!!!");
        }

        if (StringUtils.isEmpty(openid)) {
            throw new ValidationException(300, "openid不允许为空，请重新登录!!!");
        }

        //3：开始对你token和你用户名进行token校验，如果正常直接返回，如果不正常抛出异常
        AuthResponse authResponse = jwtService.verifyOpenid(token, openid);
        // 如果不等于1，说明token和用户名校验失败
        if (authResponse.getCode() != 1L) {
            log.error("token invalid error");
            throw new ValidationException(300, "token valid fail!!!");
        }

        // 4：把根据openid查询用户信息
        User userByOpenid = userService.getUserByOpenid("ofMLq0GZZZzUd5aZ2JgnlhGAekTc");
        if (userByOpenid == null) {
            log.error("user invalid error");
            throw new ValidationException(300, "user valid fail!!!");
        }

        // 放作用域
        //request.setAttribute("user",userByOpenid);
        // 用线程副本
        UserThreadLocal.put(userByOpenid);
    }


    // 小坑
    @Override
    public int getOrder() {
        return 999999;
    }

}
